Matrices y espacios vectoriales

Las matrices en gap se representan como una lista de listas, de forma que todas ellas tengan la misma longitud y el mismo tipo de elementos. Por tanto hay que tener cuidado con qué comandos son ‘destructivos’. La suma, producto e inverso se calculan con las operaciones usuales.

Esto cambiará en las versiones futuras de gap (véase el proyecto MatrixObj).

a:=[[1,2],[3,4]];
b:=[[5,6],[7,8]];
[ [ 1, 2 ], [ 3, 4 ] ]
[ [ 5, 6 ], [ 7, 8 ] ]
a+b;
[ [ 6, 8 ], [ 10, 12 ] ]
a*b;
[ [ 19, 22 ], [ 43, 50 ] ]
a^(-1);
[ [ -2, 1 ], [ 3/2, -1/2 ] ]
a^0;
[ [ 1, 0 ], [ 0, 1 ] ]

Y el determinante

Determinant(a);
-2

Para calcular la forma normal de Hermite sobre los enteros disponemos de la siguiente función.

a:=[[1,2,3],[4,5,6]];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
HermiteNormalFormIntegerMat(a);
[ [ 1, 2, 3 ], [ 0, 3, 6 ] ]

Si queremos las matrices te transformaciones para llegar a la forma de Hermite:

HermiteNormalFormIntegerMatTransform(a);
rec( normal := [ [ 1, 2, 3 ], [ 0, 3, 6 ] ], rank := 2, rowC := [ [ 1, 0 ], [ 0, 1 ] ], rowQ := [ [ 1, 0 ], [ 4, -1 ] ], rowtrans := [ [ 1, 0 ], [ 4, -1 ] ] )

Si queremos hacer el cálculo sobre los racionales, usamos TriangulizeMat, pero ojo, que esta función destruye el argumento que le pasamos.

a:=[[1,2,3],[4,5,6]];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
TriangulizeMat(a);
a;
[ [ 1, 0, -1 ], [ 0, 1, 2 ] ]

Con TriangulizedMat el argumento no se modifica.

a:=[[1,2,3],[4,5,6]];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
TriangulizedMat(a);
[ [ 1, 0, -1 ], [ 0, 1, 2 ] ]
P:=PolynomialRing(Rationals,"x");;
a:=[[1,2,3],[4,5,x]];
[ [ 1, 2, 3 ], [ 4, 5, x ] ]
x:=Indeterminate(Rationals,"x");;
TriangulizedMat(a);
Error, no method found! For debugging hints type ?Recovery from NoMethodFound
Error, no 1st choice method found for `TriangulizedMat' on 1 arguments at /home/pedro/lib/gap-4.10.0/lib/methsel2.g:250 called from
<function "HANDLE_METHOD_NOT_FOUND">( <arguments> )
 called from read-eval loop at stream:1

El error se debe a que en este caso a es una lista de listas con enteros (que no se consideran polinomios) y un polinomio (x). Para arreglar esto, multiplicamos toda la matriz por One(P) convirtiendo todos los enteros en polinomios (constantes).

TriangulizedMat(a*One(P));
[ [ 1, 0, 2/3*x-5 ], [ 0, 1, -1/3*x+4 ] ]

Si queremos hacer las cuentas sobre un cuerpo finito, podemos usar el comando GF (cuerpo de Galois), y luego embeber la matriz dada en dicho cuerpo con One.

F:=GF(7);
GF(7)
a:=[[1,2,3],[4,5,6]];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
az7:=a*One(F);
[ [ Z(7)^0, Z(7)^2, Z(7) ], [ Z(7)^4, Z(7)^5, Z(7)^3 ] ]
TriangulizeMat(az7);
az7;
[ [ Z(7)^0, 0*Z(7), Z(7)^3 ], [ 0*Z(7), Z(7)^0, Z(7)^2 ] ]
TeachingMode(true);
#I  Teaching mode is turned ON

az7;
[ [ ZmodnZObj(1,7), ZmodnZObj(0,7), ZmodnZObj(6,7) ], [ ZmodnZObj(0,7), ZmodnZObj(1,7), ZmodnZObj(2,7) ] ]
a;
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]

Nótese que a no ha sido destruida, pues la operación a*One(F) genera una nueva matriz a partir de ella.

Sistemas de ecuaciones

Para encontrar una solución de un sistema de la forma \(x A=b\) usando gap, utilizamos el comando SolutionMat junto con NullspaceMat. El primero nos da una solución particular (en caso de existir) y el segundo nos da las soluciones del sistema homogéneo asociado.

a:=[[1,2,3],[4,5,6]]; b:=[1,1,1];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
[ 1, 1, 1 ]
SolutionMat(a,b);
[ -1/3, 1/3 ]
NullspaceMat(a);
[  ]

Con lo que en este caso la solución es única.

a:=[[1,2],[3,4],[5,6]];b:=[1,1];
[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
[ 1, 1 ]
SolutionMat(a,b);
[ -1/2, 1/2, 0 ]
NullspaceMat(a);
[ [ 1, -2, 1 ] ]

Y en este caso las soluciones son de la forma \((-\frac{1}2,\frac{1}2,0)+\lambda (1,-2,1)\).

En caso de no existir solución recibimos un fail a cambio.

a:=[[1,2,3],[4,5,6]]; b:=[1,1,2];
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
[ 1, 1, 2 ]
SolutionMat(a,b);
fail

También podemos hacer las cuentas sobre un cuerpo finito.

a:=[[1,2],[3,4],[5,6]];b:=[1,1];
[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
[ 1, 1 ]
F:=GF(5);
GF(5)
SolutionMat(a*One(F),b*One(F));
[ ZmodnZObj(2,5), ZmodnZObj(3,5), ZmodnZObj(0,5) ]

Espacios vectoriales

gap ya sabe que algunos de sus objetos son espacios vectoriales, y además podemos definir nuevos espacios vectoriales de diferentes formas.

InstallMethod(ViewString, [IsPolynomial], String);
InstallMethod(ViewString, [IsVectorSpace], function(v) return("Espacio vectorial"); end);
InstallMethod(ViewString, [IsSubspacesVectorSpace], function(v) return("Subespacios vectoriales"); end);
InstallMethod(ViewString, [IsGeneralMapping], function(v) return("Aplicación"); end);
x:=Indeterminate(Rationals,"x");
x
P:=PolynomialRing(Rationals,[x]);
Rationals[x]
IsVectorSpace(P);
true
IsVectorSpace(Rationals^4);
true
V:=VectorSpace(Rationals, [[1,2,3],[4,5,6]]);
Espacio vectorial
[3,3,3] in V;
true
B:=Basis(V);
[ [ 1, 2, 3 ], [ 0, 1, 2 ] ]
Dimension(V);
2

También podemos calcular los coeficientes de un vector respecto de una base, o las combinaciones lineales de los elementos de una base.

Coefficients(B,[1,1,1]);
[ 1, -1 ]
Coefficients(B,[1,0,1]);
fail
LinearCombination(B,[1,1]);
[ 1, 3, 5 ]

Los subespacios se pueden definir con la orden Subspace, y los cocientes se hacen como en grupos.

V:=Rationals^4;
( Rationals^4 )
W:=Subspace(V,[[1,2,3,4],[1,1,1,1]]);
Espacio vectorial

Podemos hacer cocientes:

V/W;
( Rationals^2 )

También intersecciones y sumas

U:=Subspace(V,[[3,3,3,3],[2,2,0,0]]);
Espacio vectorial
Intersection(U,W);
Espacio vectorial
Basis(Intersection(U,W));
[ [ 1, 1, 1, 1 ] ]
Basis(U+W);
[ [ 1, 1, 1, 1 ], [ 0, 1, 0, 1 ], [ 0, 0, 1, 1 ] ]

Si nuestro espacio vectorial es finito, podemos calcular todos sus subespacios.

sz22:= Subspaces(GF(2)^2);
Subespacios vectoriales
List(sz22,w->BasisVectors(Basis(w)));
[ [  ], [ [ ZmodnZObj(1,2), ZmodnZObj(0,2) ] ], [ [ ZmodnZObj(1,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(1,2), ZmodnZObj(0,2) ], [ ZmodnZObj(0,2), ZmodnZObj(1,2) ] ] ]
List(sz22,w->Basis(w));
[ [  ], [ [ ZmodnZObj(1,2), ZmodnZObj(0,2) ] ], [ [ ZmodnZObj(1,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(1,2), ZmodnZObj(0,2) ], [ ZmodnZObj(0,2), ZmodnZObj(1,2) ] ] ]
List(sz22,Elements);
[ [ [ ZmodnZObj(0,2), ZmodnZObj(0,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(0,2) ], [ ZmodnZObj(1,2), ZmodnZObj(0,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(0,2) ], [ ZmodnZObj(1,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(0,2) ], [ ZmodnZObj(0,2), ZmodnZObj(1,2) ] ], [ [ ZmodnZObj(0,2), ZmodnZObj(0,2) ], [ ZmodnZObj(0,2), ZmodnZObj(1,2) ], [ ZmodnZObj(1,2), ZmodnZObj(0,2) ], [ ZmodnZObj(1,2), ZmodnZObj(1,2) ] ] ]

Para definir una aplicación lineal entre subespacios podemos usar la función LeftModuleGeneralMappingByImages, pues en gap, los espacios vectoriales son considerados como módulos a izquierda.

Un complemento de un subespacio \(W\) de \(V\) lo podemos calcular usando el homomorfismo natural de \(V\) a \(V/W\).

V:=Rationals^4;
W:=Subspace(V,[[1,0,0,0],[0,0,0,1]]);
p:=NaturalHomomorphismBySubspace(V,W);
( Rationals^4 )
Espacio vectorial
Aplicación
PreImagesRepresentative(p,[1,0]);
[ 0, 1, 0, 0 ]
PreImagesRepresentative(p,[0,1]);
[ 0, 0, 1, 0 ]

Al no ser la aplicación biyectiva no podemos usar PreImage a secas.

V1:=Rationals^4;;
V2:=VectorSpace(Rationals,[[1,2],[3,4]]);;
f:=LeftModuleGeneralMappingByImages(V1,V2,Basis(V1), [[1,2],[3,4],[1,0],[0,1]]);
( Rationals^4 )
Espacio vectorial
Aplicación
Image(f);
Espacio vectorial
Basis(Image(f));
[ [ 1, 2 ], [ 0, 1 ] ]
Basis(Kernel(f));
[ [ 1, -1/2, 1/2, 0 ], [ 0, 1, -3, -4 ] ]

Y teorema de isomorfía al canto…

V1/Kernel(f)=Image(f);
true

También podemos usar notación matricial.

g:=LeftModuleHomomorphismByMatrix(Basis(V1),[[1,0],[0,1],[1,1],[-1,1]], Basis(V2));
Aplicación
Image(g,[1,2,3,4]);
[ 0, 9 ]
IsInjective(g);
false
IsSurjective(g);
true

Diagonalización

Veamos cómo calcular el polinomio característico y sus ceros.

a:=[[1,0,1],[3,1,0],[0,1,0]]*One(GF(7));
[ [ ZmodnZObj(1,7), ZmodnZObj(0,7), ZmodnZObj(1,7) ], [ ZmodnZObj(3,7), ZmodnZObj(1,7), ZmodnZObj(0,7) ], [ ZmodnZObj(0,7), ZmodnZObj(1,7), ZmodnZObj(0,7) ] ]
p:=CharacteristicPolynomial(a);
x_1^3+ZmodnZObj(5,7)*x_1^2+x_1+ZmodnZObj(4,7)
rs:=RootsOfPolynomial(p);
[ ZmodnZObj(6,7), ZmodnZObj(5,7), ZmodnZObj(5,7) ]

Comprobemos si a es diagonalizable, para ello calculamos sus subespacios propios.

List(Set(rs),r->NullspaceMat(TransposedMat(a-r*a^0)));
[ [ [ ZmodnZObj(3,7), ZmodnZObj(6,7), ZmodnZObj(1,7) ] ], [ [ ZmodnZObj(2,7), ZmodnZObj(5,7), ZmodnZObj(1,7) ] ] ]

Como vemos, el elemento \(5\in \mathbb{Z}_7\) es una raíz doble cuyo subespacio propio tiene dimensión uno, por lo que la matriz no es diagonalizable.

Veamos otro ejemplo que sí es diagonalizable.

a:=[ [ -1, 0, -1 ], [ 0, -2, 0 ], [ -3, 0, 1 ] ];
[ [ -1, 0, -1 ], [ 0, -2, 0 ], [ -3, 0, 1 ] ]
p:=CharacteristicPolynomial(a);
x^3+2*x^2-4*x-8
rs:=RootsOfPolynomial(p);
[ 2, -2, -2 ]
sp:=List(Set(rs),r->NullspaceMat(TransposedMat(a-r*a^0)));
[ [ [ 0, 1, 0 ], [ 1, 0, 1 ] ], [ [ -1/3, 0, 1 ] ] ]
P:=TransposedMat(Concatenation(sp));
[ [ 0, 1, -1/3 ], [ 1, 0, 0 ], [ 0, 1, 1 ] ]
P^(-1)*a*P;
[ [ -2, 0, 0 ], [ 0, -2, 0 ], [ 0, 0, 2 ] ]